
#ifndef P2P_HANDSHAKE_HPP
#define P2P_HANDSHAKE_HPP

#include <memory>
#include <chrono>
#include "Common.hpp"
#include "Host.hpp"
#include "../PublicKey.hpp"
#include "../PrivateKey.hpp"
#include "../muduo/net/InetAddress.h"
#include "../muduo/net/TcpConnection.h"
#include "../spdlog/spdlog.h"

class Handshake: public std::enable_shared_from_this<Handshake>
{
public:
    // Setup incoming connection.
    Handshake(Host* host, muduo::net::TcpConnectionPtr conn,
                std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("Handshake")):
                    host_(host),conn_(conn),originated_(false),logger_(logger){}
    // Setup outbound connection.
    Handshake(Host* host, muduo::net::TcpConnectionPtr conn, NodeID remote, 
                std::shared_ptr<spdlog::logger> logger = spdlog::stdout_color_mt("Handshake")):
                    host_(host), conn_(conn), originated_(true), remote_(remote),logger_(logger){}

    virtual ~Handshake() = default;

    // Start handshake
    void start();

    // Aborts the handshake
    void cancel();
protected:
    enum State
    {
        Error = -1,
        New,
        AckAuth,
        WriteHello,
        ReadHello,
        StartSession
    };

	/// Write Auth message to socket and transitions to AckAuth.
    void writeAuth();

	/// Reads Auth message from socket and transitions to AckAuth.
    void readAuth();

	/// Derives ephemeral secret from signature and sets members after Auth has been decrypted.
	void setAuthValues(std::vector<unsigned char> const& sig, PublicKey const& remotePubk, uint64_t remoteVersion);
	
    /// Write Ack message to socket and transitions to WriteHello.
	void writeAck();

    /// Reads Auth message from socket and transitions to WriteHello.
	void readAck();

    /// Closes connection and ends transitions.
	void error();

    /// Performs transition for m_nextState.
	virtual void transition();

    /// Timeout for remote to respond to transition events. Enforced by m_idleTimer and refreshed by transition().
    const int64_t timeout_ =  1800; //milliseconds

    State nextState_ = New; ///< Current or expected state of transition.
    bool cancel_ = false;   ///< Will be set to true if connection was canceled.

    Host* host_;
    muduo::net::TcpConnectionPtr conn_;
    /// Node id of remote host for socket.
	NodeID remote_;				///< Public address of remote host.
	bool originated_;		///< True if connection is outbound.


    /// Buffers for encoded and decoded handshake phases
	bytes auth_;					///< Plaintext of egress or ingress Auth message.
	bytes authCipher_;				///< Ciphertext of egress or ingress Auth message.
	bytes ack_;					///< Plaintext of egress or ingress Ack message.
	bytes ackCipher_;				///< Ciphertext of egress or ingress Ack message.
	bytes handshakeOutBuffer_;		///< Frame buffer for egress Hello packet.
	bytes handshakeInBuffer_;		///< Frame buffer for ingress Hello packet.


	PublicKey ecdheRemote_;			///< Remote ephemeral public key.
	// h256 m_remoteNonce;				///< Nonce generated by remote host for handshake.
	uint64_t remoteVersion_;

    std::shared_ptr<spdlog::logger> logger_;
};

#endif //P2P_HANDSHAKE_HPP